/**
 * 
 */
package org.maverick.persistence.spring.jpa;

import java.io.Serializable;
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.PersistenceException;
import javax.persistence.Query;

import org.maverick.bean.EntityBean;
import org.maverick.persistence.DAO;
import org.maverick.persistence.filter.QueryFilter;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

@Repository
public class PersistenceDefaultDAO<T> implements DAO<T>, Serializable {

	private static final long serialVersionUID = 1L;
	
	protected EntityManager entityManager;
	private Class entityClass;
	
	public PersistenceDefaultDAO() {
		super();
	}
	
	public PersistenceDefaultDAO(Class entityClass) {
		super();
		this.entityClass = entityClass;
	}

	@PersistenceContext
    public void setEntityManager(EntityManager entityManager) {
        this.entityManager = entityManager;
    }
	
    @Override
    @Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)
	public void save(T entity) {
    	try {
    		this.entityManager.merge(entity);
		} catch (Exception e) {
			throw new PersistenceException(e);
		}
	}

	@Override
	@Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)
	public void remove(T entity) {
		try {
			T entityBean = (T) getEntity(((EntityBean)entity).getId());
	        if (entityBean != null) 
	        	this.entityManager.remove(entityBean);	        
		} catch (Exception e) {
			throw new PersistenceException(e);
		}
	}

	@SuppressWarnings("unchecked")
	@Override
	public T getEntity(Long id) {
		return (T) this.entityManager.find(this.entityClass, id);
	}

	@SuppressWarnings({ "rawtypes", "unchecked" })
	@Override
	public T getEntity(Class entityClass, Long id) {
		return (T) this.entityManager.find(entityClass, id);
	}

	@Override
	public Long count() {
		return count(null);
	}

	@Override
	public List<T> find() {
		return find(null);
	}

	@Override
	public Long count(QueryFilter queryFilter) {
		
		try {				
					
			// Remove todos os filtros nulos da lista...
			//queryFilter.removeFiltersNull();
			
			String hql = QueryFilter.SELECT_COUNT.replaceAll("class", entityClass.getName());
			
			// Se o Map parameters for diferente de nulo e não estiver vazio, será adicionada os parametros da clausula where.
			if (queryFilter.getFilters() != null && !queryFilter.getFilters().isEmpty()){
				
				hql += QueryFilter.WHERE;
				
				int sizeListFilters = queryFilter.getFilters().size();
				
				// Percorre o array das chaves e adiciona os criterios na pesquisa 
				for (int i = 0; i < sizeListFilters; i++){
					
					if (queryFilter.getFilters().get(i).getField().equals(QueryFilter.OPEN)){
						hql += " ( ";
					}
					else if (queryFilter.getFilters().get(i).getField().equals(QueryFilter.CLOSE)){
						hql += " ) ";
					}
					else if (queryFilter.getFilters().get(i).getField().equals(QueryFilter.OR)){
						hql += QueryFilter.OR;
					}
					else if (queryFilter.getFilters().get(i).getField().contains(QueryFilter.IS_NOT_NULL) || queryFilter.getFilters().get(i).getField().contains(QueryFilter.IS_NULL)){
						hql += queryFilter.getFilters().get(i);
					}
					else if (queryFilter.getFilters().get(i).getField().contains(QueryFilter.DIFFERENT) || queryFilter.getFilters().get(i).getField().contains(QueryFilter.MAIOR) || 
							 queryFilter.getFilters().get(i).getField().contains(QueryFilter.MENOR) || queryFilter.getFilters().get(i).getField().contains(QueryFilter.MAIOR_OU_IGUAL)
							 || queryFilter.getFilters().get(i).getField().contains(QueryFilter.MENOR_OU_IGUAL)){
						
						hql += queryFilter.getFilters().get(i) + " :parameter" + i;
					}
					else if (queryFilter.getFilters().get(i).getField().contains(QueryFilter.LIKE)){
						hql += "upper(" + queryFilter.getFilters().get(i) + " upper(:parameter" + i + ")";
					}
					else
						hql += queryFilter.getFilters().get(i) + " = :parameter" + i;
					
					if (i < (queryFilter.getFilters().size() - 1)){
						
						if (!queryFilter.getFilters().get(i).getField().equals(QueryFilter.OPEN) && !queryFilter.getFilters().get(i+1).getField().equals(QueryFilter.CLOSE) && 
						    !queryFilter.getFilters().get(i).getField().equals(QueryFilter.OR) && !queryFilter.getFilters().get(i+1).getField().equals(QueryFilter.OR)){
								hql += " and ";
						}
					}						
				}
			}
			
			Query query = entityManager.createQuery(hql); 
            
			int sizeListfilters = queryFilter.getFilters().size();
			
			// Se a lista de filtros for diferente de nulo e não estiver vazio, será adicionada os parametros a query
			if (queryFilter.getFilters() != null && !queryFilter.getFilters().isEmpty()){
				for (int x = 0 ; x < sizeListfilters; x++){
					
					if (!queryFilter.getFilters().get(x).getField().equals(QueryFilter.OPEN) && !queryFilter.getFilters().get(x).getField().equals(QueryFilter.CLOSE) &&
							!queryFilter.getFilters().get(x).getField().equals(QueryFilter.OR) && !queryFilter.getFilters().get(x).getField().contains(QueryFilter.IS_NULL) 
							&& !queryFilter.getFilters().get(x).getField().contains(QueryFilter.IS_NOT_NULL)){
						
						if (queryFilter.getFilters().get(x).getField().contains(QueryFilter.LIKE))
							query.setParameter("parameter"+x, "%"+queryFilter.getFilters().get(x).getValue()+"%");
						else	
							query.setParameter("parameter"+x, queryFilter.getFilters().get(x).getValue());
					}
				}
			}		
			
            return (Long) query.getSingleResult();

		} catch (Exception e) {
            e.printStackTrace();
        } 
		return null;
	}

	@SuppressWarnings("unchecked")
	@Override
	public List<T> find(QueryFilter queryFilter) {
				
		try {				
			
			// Remove todos os filtros nulos da lista...
			//queryFilter.removeFiltersNull();
			
			String hql = QueryFilter.SELECT.replaceAll("class", entityClass.getName());
			
			// Se o Map parameters for diferente de nulo e não estiver vazio, será adicionada os parametros da clausula where.
			if (queryFilter != null && (queryFilter.getFilters() != null && !queryFilter.getFilters().isEmpty())){
				
				hql += QueryFilter.WHERE;
				
				int sizeListFilters = queryFilter.getFilters().size();
				
				// Percorre o array das chaves e adiciona os criterios na pesquisa 
				for (int i = 0; i < sizeListFilters; i++){
					
					if (queryFilter.getFilters().get(i).getField().equals(QueryFilter.OPEN)){
						hql += " ( ";
					}
					else if (queryFilter.getFilters().get(i).getField().equals(QueryFilter.CLOSE)){
						hql += " ) ";
					}
					else if (queryFilter.getFilters().get(i).getField().equals(QueryFilter.OR)){
						hql += QueryFilter.OR;
					}
					else if (queryFilter.getFilters().get(i).getField().contains(QueryFilter.IS_NOT_NULL) || queryFilter.getFilters().get(i).getField().contains(QueryFilter.IS_NULL)){
						hql += queryFilter.getFilters().get(i);
					}
					else if (queryFilter.getFilters().get(i).getField().contains(QueryFilter.DIFFERENT) || queryFilter.getFilters().get(i).getField().contains(QueryFilter.MAIOR) || 
							 queryFilter.getFilters().get(i).getField().contains(QueryFilter.MENOR) || queryFilter.getFilters().get(i).getField().contains(QueryFilter.MAIOR_OU_IGUAL)
							 || queryFilter.getFilters().get(i).getField().contains(QueryFilter.MENOR_OU_IGUAL)){
						
						hql += queryFilter.getFilters().get(i) + " :parameter" + i;
					}
					else if (queryFilter.getFilters().get(i).getField().contains(QueryFilter.LIKE)){
						hql += "upper(" + queryFilter.getFilters().get(i) + " upper(:parameter" + i + ")";
					}
					else
						hql += queryFilter.getFilters().get(i) + " = :parameter" + i;
					
					if (i < (queryFilter.getFilters().size() - 1)){
						
						if (!queryFilter.getFilters().get(i).getField().equals(QueryFilter.OPEN) && !queryFilter.getFilters().get(i+1).getField().equals(QueryFilter.CLOSE) && 
						    !queryFilter.getFilters().get(i).getField().equals(QueryFilter.OR) && !queryFilter.getFilters().get(i+1).getField().equals(QueryFilter.OR)){
								hql += " and ";
						}
					}						
				}
			}
			
			// Se o orderBy estiver preenchido, sera adicionado a consulta			
			if (queryFilter != null && (queryFilter.getOrderBy() != null && !queryFilter.getOrderBy().trim().equals(""))) {
	            hql += " " + QueryFilter.ORDER_BY.replaceAll("orderBy", queryFilter.getOrderBy());
	        }
			
			Query query = entityManager.createQuery(hql); 
            
			if (queryFilter != null && (queryFilter.getFilters() != null && !queryFilter.getFilters().isEmpty())){
			
				int sizeListfilters = queryFilter.getFilters().size();
				
				// Se a lista de filtros for diferente de nulo e não estiver vazio, será adicionada os parametros a query
				if (queryFilter.getFilters() != null && !queryFilter.getFilters().isEmpty()){
					for (int x = 0 ; x < sizeListfilters; x++){
						
						if (!queryFilter.getFilters().get(x).getField().equals(QueryFilter.OPEN) && !queryFilter.getFilters().get(x).getField().equals(QueryFilter.CLOSE) &&
								!queryFilter.getFilters().get(x).getField().equals(QueryFilter.OR) && !queryFilter.getFilters().get(x).getField().contains(QueryFilter.IS_NULL) 
								&& !queryFilter.getFilters().get(x).getField().contains(QueryFilter.IS_NOT_NULL)){
							
							if (queryFilter.getFilters().get(x).getField().contains(QueryFilter.LIKE))
								query.setParameter("parameter"+x, "%"+queryFilter.getFilters().get(x).getValue()+"%");
							else	
								query.setParameter("parameter"+x, queryFilter.getFilters().get(x).getValue());
						}
					}
				}		
			}	
			
			// Se firstResult e maxResults estiver preenchido, sera adicionado a query
			if (queryFilter != null && 
			   ((queryFilter.getFirstResult() != null && queryFilter.getFirstResult() != QueryFilter.NO_PAGINATOR) && 
				(queryFilter.getFirstResult() != null && queryFilter.getMaxResults() != QueryFilter.NO_PAGINATOR))){
				query.setFirstResult(queryFilter.getFirstResult());
				query.setMaxResults(queryFilter.getMaxResults());
			}
			
            return query.getResultList();
        
		} catch (Exception e) {
            e.printStackTrace();
        } 
		
		return null;
	}

}